package jinyilw.swing.rlaf.ui;

import jinyilw.common.OSTools;
import jinyilw.swing.ColorTools;
import jinyilw.swing.ComponentTools;
import jinyilw.swing.NativeTools;
import jinyilw.swing.ScreenTools;
import jinyilw.swing.WindowTools;
import jinyilw.swing.graphics.RenderingConfig;
import jinyilw.swing.rlaf.theme.RRootPaneSet;
import jinyilw.swing.rlaf.theme.RThemeColor;
import jinyilw.swing.rlaf.utils.RLafUtils;

import javax.swing.JComponent;
import javax.swing.JLayeredPane;
import javax.swing.JMenuBar;
import javax.swing.JRootPane;
import javax.swing.SwingUtilities;
import javax.swing.event.MouseInputListener;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicRootPaneUI;
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Component;
import java.awt.Composite;
import java.awt.Container;
import java.awt.Cursor;
import java.awt.Dialog;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Insets;
import java.awt.LayoutManager;
import java.awt.LayoutManager2;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Window;
import java.awt.event.InputEvent;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.geom.RoundRectangle2D;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;

/**
 * @author Jinyi
 */
public class RRootPaneUI extends BasicRootPaneUI
{
	public static final int NONE = 0;
	public static final int FRAME = 1;
	public static final int PLAIN_DIALOG = 2;
	public static final int INFORMATION_DIALOG = 3;
	public static final int ERROR_DIALOG = 4;
	public static final int COLOR_CHOOSER_DIALOG = 5;
	public static final int FILE_CHOOSER_DIALOG = 6;
	public static final int QUESTION_DIALOG = 7;
	public static final int WARNING_DIALOG = 8;
	// public static final int BRIGHTER_STYLE = 9;
	// public static final int INTERNAL_FRAME = 10;

	public static final Dimension MINIMUM_SIZE = new Dimension(160, 120);
	public static final Dimension MAXIMUM_SIZE = ScreenTools
			.getDefaultScreenSize();

	private static final int CORNER_DRAG_WIDTH = 16;
	private static final int BORDER_DRAG_THICKNESS = 5;
	private static final AlphaComposite alphaComposite = ComponentTools.alphaComposite;
	private static final RRootPaneSet rootPaneSet = RRootPaneSet.getRootPaneSet();

	private static final boolean enableOptionStyle = OSTools.isMac();

	private MouseInputListener mouseInputListener;
	private LayoutManager layoutManager, savedOldLayout;
	private WindowHandler windowHandler;
	private Window window;
	private RTitlePane titlePane;
	private JRootPane root;
	private Container customMenubar;
	private Cursor savedCursor;

	private float alpha = 1f;
	private int brightness = 0;
	private int brighter = 0;
	private int contentSection = 0;
	private Image customImage;

	private boolean rounded = true;
	private final int arc = 8;
	private int decorationStyle = NONE;
	private boolean isOptionStyle;
	private boolean transparent;
	private boolean isActive;
	private volatile boolean isFullScreenMode;

	public static ComponentUI createUI(JComponent c)
	{
		return new RRootPaneUI();
	}

	void setDecorationStyle(int style)
	{
		if (decorationStyle == style)
			return;
		if (decorationStyle != NONE && style == NONE)
			uninstallClientDecorations(root);
		else if (decorationStyle == NONE)
			installClientDecorations(root);
		if (style != NONE && style != FRAME && style != PLAIN_DIALOG)
		{
			WindowTools.setWindowOpacity(window, 0.97f);
			//			if (window != null)
			//				window.setOpacity(0.97f);
			if (style == FILE_CHOOSER_DIALOG || style == COLOR_CHOOSER_DIALOG)
				brighter = 100;
			else
				brighter = 150;
		}
		decorationStyle = style;
	}

	public void setOptionStyle(boolean b)
	{
		if (enableOptionStyle)
			isOptionStyle = b;
	}

	public void setOptionStyle(int br, float al)
	{
		if (enableOptionStyle)
		{
			isOptionStyle = true;
			brightness = br;
			alpha = al;
			if (al < 1f && !transparent && window != null)
			{
				window.setBackground(ColorTools.transparentColor);
				transparent = true;
			}
		} else
		{
			brightness = br;
			WindowTools.setWindowOpacity(window, al);
			//			if (window != null)
			//				window.setOpacity(al);
		}
	}

	public void setTransBrightStyle(boolean b)
	{
		if (b)
		{
			WindowTools.setWindowOpacity(window, 0.97f);
			//			if (window != null)
			//				window.setOpacity(0.97f);
			brighter = 150;
		} else
		{
			WindowTools.setWindowOpacity(window, 1);
			//			if (window != null)
			//				window.setOpacity(1);
			brighter = 0;
		}
	}

	public void setBrightness(int br)
	{
		brightness = br;
	}

	public void setTransparent(boolean b)
	{
		if (!RLafUtils.transparentEnable || isOptionStyle)
			return;
		if (transparent == b || window == null)
			return;
		transparent = b;
		if (b)
		{
			window.setBackground(ColorTools.transparentColor);
			// root.getLayeredPane().setOpaque(true);
		} else
		{
			window.setBackground(rootPaneSet.bgColor);
			root.getLayeredPane().setOpaque(false);
			Container c = root.getContentPane();
			if (c instanceof JComponent)
				((JComponent) c).setOpaque(false);
		}
		// System.out.println(window.getName() + " setTransparent");
	}

	public boolean isTransparent()
	{
		return transparent;
	}

	private void initBackground()
	{
		if (isOptionStyle || window == null)
			return;
		if (RLafUtils.transparentEnable)
		{
			if (!transparent)
			{
				transparent = true;
				window.setBackground(ColorTools.transparentColor);
			}
		} else
		{
			window.setBackground(rootPaneSet.bgColor);
		}
		// System.out.println(window.getName() + " initBackground");
	}

	public void setCustomImage(Image img)
	{
		customImage = img;
		root.repaint();
	}

	public void setCustomMenuBar(Container c)
	{
		Container oldBar = customMenubar;
		if ((oldBar == null && c == null)
				|| (oldBar != null && oldBar.equals(c)))
			return;
		JLayeredPane layeredPane = root.getLayeredPane();
		if (oldBar != null)
		{
			oldBar.setVisible(false);
			layeredPane.remove(oldBar);
		}
		if (c != null)
		{
			layeredPane.add(c, JLayeredPane.FRAME_CONTENT_LAYER);
			c.setVisible(true);
		}
		customMenubar = c;
	}

	public void setContentSection(int section)
	{
		if (section == contentSection || section < 0)
			return;
		contentSection = section;
	}

	public int getContentSection()
	{
		return contentSection;
	}

	public void setRounded(boolean b)
	{
		if (rounded == b)
			return;
		rounded = b;
		if (window != null)
		{
			if (rounded)
				window.setShape(new RoundRectangle2D.Double(0, 0,
						window.getWidth(), window.getHeight(), arc, arc));
			else
				window.setShape(null);
		}
	}

	@Override
	public void update(Graphics g, JComponent c)
	{
		if (decorationStyle == NONE)
			return;
		int w = c.getWidth(), h = c.getHeight();
		Graphics2D g2d = (Graphics2D) g;
		Composite composite = g2d.getComposite();
		if (isOptionStyle)
		{
			g2d.setComposite(
					alphaComposite.derive(Math.min(alpha, RLafUtils.universeAlpha)));
			g2d.drawImage(rootPaneSet.bgImage, 0, 0, w, h, null);
			g2d.setComposite(composite);
			int b = Math.max(brightness, RLafUtils.universeBright);
			if (b > 10)
			{
				g2d.setColor(ColorTools.getAlphaColor(rootPaneSet.bgColor, b));
				g2d.fillRect(0, 0, w, h);
			}
		} else
		{
			g2d.setComposite(alphaComposite
					.derive(transparent ? RLafUtils.universeAlpha : 1f));
			if (customImage != null)
				g2d.drawImage(customImage, 0, 0, w, h, null);
			else
				g2d.drawImage(rootPaneSet.bgImage, 0, 0, w, h, null);
			g2d.setComposite(composite);
			int b = Math.max(brightness, RLafUtils.universeBright);
			b += brighter;
			if (b > 10)
			{
				g2d.setColor(ColorTools.getAlphaColor(rootPaneSet.bgColor, b));
				g2d.fillRect(0, 0, w, h);
			}
		}
		paintBorder(g2d);
		//		if (isActive)
		//		{
		//			Object savedRederingHint = g2d
		//					.getRenderingHint(RenderingHints.KEY_ANTIALIASING);
		//			g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
		//					RenderingHints.VALUE_ANTIALIAS_ON);
		//			g2d.setColor(RThemeColors.windowBorderColor);
		//			int ar = arc + 2;
		//			// g2d.drawRoundRect(0, 0, w - 2, h - 2, ar, ar);
		//			g2d.drawRoundRect(0, 0, w - 1, h - 1, ar, ar);
		//			g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
		//					savedRederingHint);
		//		}
	}

	@Override
	public void installUI(JComponent c)
	{
		super.installUI(c);
		root = (JRootPane) c;

		if (root.getWindowDecorationStyle() != NONE)
			installClientDecorations(root);
	}

	@Override
	public void uninstallUI(JComponent c)
	{
		super.uninstallUI(c);
		uninstallClientDecorations(root);
		layoutManager = null;
		mouseInputListener = null;
		root = null;
	}

	public void installWindowListeners(JRootPane root, Component parent)
	{
		if (parent instanceof Window)
			window = (Window) parent;

		//		String string = (window == null) ? "null" : window.getName();
		//		LogTools.info(string + " installWindowListeners");

		if (window != null)
		{
			if (mouseInputListener == null)
				mouseInputListener = createWindowMouseInputListener(root);
			window.addMouseListener(mouseInputListener);
			window.addMouseMotionListener(mouseInputListener);
			if (windowHandler == null)
				windowHandler = new WindowHandler();
			window.addWindowListener(windowHandler);
		}
	}

	public void uninstallWindowListeners(JRootPane root)
	{
		//		String string = (window == null) ? "null" : window.getName();
		//		LogTools.info(string + " uninstallWindowListeners");
		if (window != null)
		{
			window.removeMouseListener(mouseInputListener);
			window.removeMouseMotionListener(mouseInputListener);
			window.removeWindowListener(windowHandler);
			// window.removeFocusListener(focusManager);
		}
	}

	public void installLayout(JRootPane root)
	{
		Component com = root.getParent();
		//		String string = (com == null)
		//				? "null"
		//				: com.getName() == null ? com.toString() : com.getName();
		//		LogTools.info(string + " installLayout");
		if (layoutManager == null)
			layoutManager = createLayoutManager();
		savedOldLayout = root.getLayout();
		root.setLayout(layoutManager);
	}

	public void uninstallLayout(JRootPane root)
	{
		//LogTools.info(root.getParent().getName() + " uninstallLayout");
		if (savedOldLayout != null)
		{
			root.setLayout(savedOldLayout);
			savedOldLayout = null;
		}
	}

	public void installClientDecorations(JRootPane root)
	{

		Component com = root.getParent();
		//LogTools.info(com.getName() + " installTitlePane");
		// root.setBorder(new EmptyBorder(2, 2, 2, 2));
		setContentSection(2);
		if (titlePane == null)
			setTitlePane(root, createTitlePane(root));
		installWindowListeners(root, root.getParent());
		installLayout(root);
		if (window != null)
		{
			// savedCursor = window.getCursor();
			// window.setBackground(UITools.transparencyColor);
			root.revalidate();
			root.repaint();
		}
	}

	public void uninstallClientDecorations(JRootPane root)
	{
		Component com = root.getParent();
		//LogTools.info(com.getName() + " uninstallTitlePane");
		setContentSection(0);
		// root.setBorder(null);
		setTitlePane(root, null);
		uninstallWindowListeners(root);
		uninstallLayout(root);
		if (root.getWindowDecorationStyle() == NONE)
		{
			root.repaint();
			root.revalidate();
		}
		if (window != null && savedCursor != null)
		{
			window.setCursor(savedCursor);
			savedCursor = null;
		}
		window = null;
	}

	public RTitlePane createTitlePane(JRootPane root)
	{
		return new RTitlePane(root, this);
	}

	public MouseInputListener createWindowMouseInputListener(JRootPane root)
	{
		return new MouseInputHandler();
	}

	public LayoutManager createLayoutManager()
	{
		return new RRootLayout();
	}

	public void setTitlePane(JRootPane root, RTitlePane titlePane)
	{
		Container oldTitlePane = this.titlePane;
		if ((oldTitlePane == null && titlePane == null)
				|| (oldTitlePane != null && oldTitlePane.equals(titlePane)))
			return;
		JLayeredPane layeredPane = root.getLayeredPane();
		if (oldTitlePane != null)
		{
			oldTitlePane.setVisible(false);
			layeredPane.remove(oldTitlePane);
		}
		if (titlePane != null)
		{
			layeredPane.add(titlePane, JLayeredPane.FRAME_CONTENT_LAYER);
			titlePane.setVisible(true);
		}
		this.titlePane = titlePane;
	}

	private Rectangle normalBounds;
	private boolean resizable, ontop, round;
	public void setFullScreenMode(boolean b)
	{
		Frame frame = getFrame();
		if (isFullScreenMode == b || frame == null)
			return;
		isFullScreenMode = b;
		if (b)
		{
			normalBounds = frame.getBounds();
			frame.setBounds(ScreenTools.getDefaultScreenMaxBounds());
			resizable = frame.isResizable();
			ontop = frame.isAlwaysOnTop();
			frame.setResizable(false);
			frame.setAlwaysOnTop(true);
			window.removeMouseListener(mouseInputListener);
			window.removeMouseMotionListener(mouseInputListener);
			round = rounded;
			setRounded(false);
		} else
		{
			frame.setBounds(normalBounds);
			frame.setResizable(resizable);
			frame.setAlwaysOnTop(ontop);
			window.addMouseListener(mouseInputListener);
			window.addMouseMotionListener(mouseInputListener);
			setRounded(round);
		}
	}

	public RTitlePane getTitlePane()
	{
		if (titlePane instanceof RTitlePane)
			return titlePane;
		return null;
	}

	public void close()
	{
		if (titlePane != null)
			titlePane.close();
	}

	//	public void closing()
	//	{
	//		if (titlePane != null)
	//			titlePane.closing();
	//	}

	public Window getWindow()
	{
		if (window == null)
		{
			Component parent = root.getParent();
			if (parent instanceof Window)
				window = (Window) parent;
		}
		return window;
	}

	protected Frame getFrame()
	{
		getWindow();
		if (window instanceof Frame)
			return (Frame) window;
		return null;
	}

	@Override
	public void propertyChange(PropertyChangeEvent e)
	{
		super.propertyChange(e);
		String propertyName = e.getPropertyName();
		if ("windowDecorationStyle".equals(propertyName))
		{
			Component parent = root.getParent();
			//			String string = parent.getName() == null
			//					? parent.toString()
			//					: parent.getName();
			//			LogTools.info(string + " setWindowDecorationStyle");
			int style = root.getWindowDecorationStyle();
			if (decorationStyle != style)
			{
				setDecorationStyle(style);
				initBackground();
			}
		} else if ("ancestor".equals(propertyName))
		{
			Component parent = root.getParent();
			//			String string = parent.getName() == null
			//					? parent.getClass().getCanonicalName()
			//					: parent.getName();
			//			LogTools.info(string + " setAncestor");
			window = null;
			if (parent instanceof Window)
				window = (Window) parent;
		}
	}

	public void makeTransparent(boolean b)
	{
		if (b)
		{
			window.setBackground(ColorTools.transparentColor);
		} else
		{
			window.setBackground(rootPaneSet.bgColor);
			root.getLayeredPane().setOpaque(false);
			Container c = root.getContentPane();
			if (c instanceof JComponent)
				((JComponent) c).setOpaque(false);
		}
	}

	private class RRootLayout implements LayoutManager2
	{
		@Override
		public Dimension preferredLayoutSize(Container parent)
		{
			Dimension cpd, mbd, tpd;
			int cpWidth = 0, cpHeight = 0, mbWidth = 0, mbHeight = 0,
					tpHeight = 0;
			int cmWidth = 0, cmHeight = 0;
			Insets i = parent.getInsets();
			JRootPane root = (JRootPane) parent;

			if (root.getContentPane() != null)
				cpd = root.getContentPane().getPreferredSize();
			else
				cpd = root.getSize();
			if (cpd != null)
			{
				cpWidth = cpd.width;
				cpHeight = cpd.height;
			}
			JMenuBar menuBar = root.getJMenuBar();
			if (menuBar != null && menuBar.isShowing())
			{
				mbd = menuBar.getPreferredSize();
				if (mbd != null)
				{
					mbWidth = mbd.width;
					mbHeight = mbd.height;
				}
			}
			if (customMenubar != null)
			{
				cmWidth = customMenubar.getWidth();
				cmHeight = customMenubar.getHeight();
			}
			if (titlePane != null)
			{
				tpd = titlePane.getPreferredSize();
				if (tpd != null)
					tpHeight = tpd.height;
			}

			return new Dimension(
					Math.max(Math.max(cpWidth, mbWidth), cmWidth) + i.left
							+ i.right + contentSection * 2,
					cpHeight + mbHeight + tpHeight + cmHeight + i.bottom
							+ contentSection);
		}

		@Override
		public Dimension minimumLayoutSize(Container parent)
		{
			return MINIMUM_SIZE;
		}

		@Override
		public Dimension maximumLayoutSize(Container target)
		{
			return MAXIMUM_SIZE;
		}

		@Override
		public void layoutContainer(Container parent)
		{
			JRootPane root = (JRootPane) parent;
			Component layeredPane = root.getLayeredPane();
			Component glassPane = root.getGlassPane();
			Component menuBar = root.getJMenuBar();
			Component contentPane = root.getContentPane();
			Rectangle b = root.getBounds();
			Insets i = root.getInsets();
			int nextY = 0;
			int w = b.width - i.right - i.left;
			int h = b.height - i.bottom;

			if (layeredPane != null)
				layeredPane.setBounds(i.left, 0, w, h);

			if (glassPane != null)
				glassPane.setBounds(i.left, 0, w, h);
			if (titlePane != null)
			{
				Dimension tpd = titlePane.getPreferredSize();
				titlePane.setBounds(0, 0, w, tpd.height);
				nextY += tpd.height;
			}
			if (menuBar != null && menuBar.isShowing())
			{
				Dimension mbd = menuBar.getPreferredSize();
				if (!NativeTools.isMacAppMenuBarEnabled())
				{
					menuBar.setBounds(0, nextY, w, mbd.height);
					nextY += mbd.height;
				}
			}
			if (customMenubar != null)
			{
				int height = customMenubar.getHeight();
				customMenubar.setBounds(0, nextY, w, height);
				nextY += height;
			}
			if (contentPane != null)
			{
				int width = w - 2 * contentSection;
				int height = h - nextY - contentSection;
				contentPane.setBounds(contentSection, nextY, width, height);
			}

			if (window != null)
			{
				if (rounded)
				{
					window.setShape(new RoundRectangle2D.Double(0, 0, b.width,
							b.height, arc, arc));
					//					System.out.println("layout setShape:" + window.getClass()
					//							+ "  style:" + decorationStyle);
				} else
					window.setShape(null);
			}
		}

		@Override
		public void addLayoutComponent(String name, Component comp)
		{
		}

		@Override
		public void removeLayoutComponent(Component comp)
		{
		}

		@Override
		public void addLayoutComponent(Component comp, Object constraints)
		{
		}

		@Override
		public float getLayoutAlignmentX(Container target)
		{
			return 0.0f;
		}

		@Override
		public float getLayoutAlignmentY(Container target)
		{
			return 0.0f;
		}

		@Override
		public void invalidateLayout(Container target)
		{
		}
	}

	private static final int[] cursorMapping = ComponentTools.cursorMapping;
	private static final int minScreenY = ScreenTools.getMinScreenY();

	private class MouseInputHandler implements MouseInputListener
	{
		private boolean isMovingWindow = false;
		private boolean isResizingWindow = false;
		private int dragCursor;
		private int dragOffsetX;
		private int dragOffsetY;
		private int dragWidth;
		private int dragHeight;
		boolean roundSetted = false;

		@Override
		public void mousePressed(MouseEvent ev)
		{
			Window w = (Window) ev.getSource();
			if (root.getWindowDecorationStyle() == NONE)
				return;
			// w.toFront();
			Point dragOffset = ev.getPoint();
			Frame f = null;
			Dialog d = null;
			if (w instanceof Frame)
				f = (Frame) w;
			else if (w instanceof Dialog)
				d = (Dialog) w;

			if (dragCursor == 0)
			{
				if ((f != null || (d != null))
						&& dragOffset.y >= BORDER_DRAG_THICKNESS
						&& dragOffset.x >= BORDER_DRAG_THICKNESS
						&& dragOffset.x < w.getWidth() - BORDER_DRAG_THICKNESS)
				{
					isMovingWindow = true;
					dragOffsetX = dragOffset.x;
					dragOffsetY = dragOffset.y;
					for (PropertyChangeListener pcl : w
							.getPropertyChangeListeners())
						pcl.propertyChange(new PropertyChangeEvent(w,
								"windowMoving", Boolean.FALSE, Boolean.FALSE));
				}
				return;
			}

			if (f != null && f.isResizable() && !titlePane.wasMaximized()
					|| (d != null && d.isResizable()))
			{
				isResizingWindow = true;
				dragOffsetX = dragOffset.x;
				dragOffsetY = dragOffset.y;
				dragWidth = w.getWidth();
				dragHeight = w.getHeight();
				for (PropertyChangeListener pcl : w
						.getPropertyChangeListeners())
					pcl.propertyChange(new PropertyChangeEvent(w,
							"windowResizing", Boolean.FALSE, Boolean.FALSE));
				if (rounded)
				{
					rounded = false;
					roundSetted = true;
				}
				if (OSTools.isMac() && transparent)
					makeTransparent(false);
			}
		}

		@Override
		public void mouseReleased(MouseEvent ev)
		{
			Object object = ev.getSource();
			if (!(object instanceof Window))
				return;
			Window w = (Window) object;
			if (dragCursor != 0 && !w.isValid())
			{
				w.validate();
				root.repaint();
			}

			for (PropertyChangeListener pcl : w.getPropertyChangeListeners())
			{
				if (isMovingWindow)
					pcl.propertyChange(new PropertyChangeEvent(w, "windowMoved",
							Boolean.FALSE, Boolean.FALSE));
				else
					pcl.propertyChange(new PropertyChangeEvent(w,
							"windowResized", Boolean.FALSE, Boolean.FALSE));
			}
			if (isResizingWindow)
			{
				if (OSTools.isMac() && transparent)
					makeTransparent(true);
				if (roundSetted)
				{
					rounded = true;
					roundSetted = false;
				}
				if (window != null && rounded)
				{
					window.setShape(new RoundRectangle2D.Double(0, 0,
							window.getWidth(), window.getHeight(), arc, arc));
				}
			}
			isMovingWindow = false;
			isResizingWindow = false;
			dragCursor = 0;
		}

		@Override
		public void mouseMoved(MouseEvent ev)
		{
			Object object = ev.getSource();
			if (!(object instanceof Window) || decorationStyle == NONE)
				return;
			if (titlePane != null && titlePane.wasMaximized())
				return;
			Window w = (Window) object;
			Frame f = null;
			Dialog d = null;
			if (w instanceof Frame)
				f = (Frame) w;
			else if (w instanceof Dialog)
				d = (Dialog) w;
			if (f != null && !f.isResizable())
				return;
			if (d != null && !d.isResizable())
				return;

			dragCursor = getCursor(calculateCorner(w, ev.getX(), ev.getY()));
			if (!isMovingWindow && dragCursor != 0)
			{
				if (savedCursor == null)
					savedCursor = w.getCursor();
				w.setCursor(Cursor.getPredefinedCursor(dragCursor));
			} else if (savedCursor != null)
			{
				w.setCursor(savedCursor);
				savedCursor = null;
			}
		}

		private void adjust(Rectangle bounds, Dimension min, int deltaX,
				int deltaY, int deltaWidth, int deltaHeight)
		{
			bounds.x += deltaX;
			bounds.y += deltaY;
			bounds.width += deltaWidth;
			bounds.height += deltaHeight;
			if (min != null)
			{
				if (bounds.width < min.width)
				{
					int correction = min.width - bounds.width;
					if (deltaX != 0)
						bounds.x -= correction;
					bounds.width = min.width;
				}
				if (bounds.height < min.height)
				{
					int correction = min.height - bounds.height;
					if (deltaY != 0)
						bounds.y -= correction;
					bounds.height = min.height;
				}
			}
		}

		@Override
		public void mouseDragged(MouseEvent ev)
		{
			Object object = ev.getSource();
			if (!(object instanceof Window))
				return;
			Window w = (Window) object;
			if (!w.isShowing())
				return;

			if (isMovingWindow)
			{
				Point location = ev.getLocationOnScreen();
				location.x -= dragOffsetX;
				location.y = Math.max(minScreenY, location.y - dragOffsetY);
				w.setLocation(location);
			} else if (dragCursor != 0)
			{
				Point pt = ev.getPoint();
				Rectangle bounds = w.getBounds();
				Rectangle startBounds = new Rectangle(bounds);
				Dimension min = MINIMUM_SIZE;
				switch (dragCursor)
				{
					case Cursor.E_RESIZE_CURSOR:
						adjust(bounds, min, 0, 0,
								pt.x + (dragWidth - dragOffsetX) - bounds.width,
								0);
						break;
					case Cursor.S_RESIZE_CURSOR:
						adjust(bounds, min, 0, 0, 0, pt.y
								+ (dragHeight - dragOffsetY) - bounds.height);
						break;
					case Cursor.N_RESIZE_CURSOR:
						adjust(bounds, min, 0, pt.y - dragOffsetY, 0,
								-(pt.y - dragOffsetY));
						break;
					case Cursor.W_RESIZE_CURSOR:
						adjust(bounds, min, pt.x - dragOffsetX, 0,
								-(pt.x - dragOffsetX), 0);
						break;
					case Cursor.NE_RESIZE_CURSOR:
						adjust(bounds, min, 0, pt.y - dragOffsetY,
								pt.x + (dragWidth - dragOffsetX) - bounds.width,
								-(pt.y - dragOffsetY));
						break;
					case Cursor.SE_RESIZE_CURSOR:
						adjust(bounds, min, 0, 0,
								pt.x + (dragWidth - dragOffsetX) - bounds.width,
								pt.y + (dragHeight - dragOffsetY)
										- bounds.height);
						break;
					case Cursor.NW_RESIZE_CURSOR:
						adjust(bounds, min, pt.x - dragOffsetX,
								pt.y - dragOffsetY, -(pt.x - dragOffsetX),
								-(pt.y - dragOffsetY));
						break;
					case Cursor.SW_RESIZE_CURSOR:
						adjust(bounds, min, pt.x - dragOffsetX, 0,
								-(pt.x - dragOffsetX),
								pt.y + (dragHeight - dragOffsetY)
										- bounds.height);
				}
				if (!bounds.equals(startBounds))
				{
					if (bounds.y < minScreenY)
					{
						int delta = minScreenY - bounds.y;
						bounds.y = minScreenY;
						bounds.height -= delta;
					}
					w.setBounds(bounds);
				}
			}
		}

		@Override
		public void mouseEntered(MouseEvent ev)
		{
			mouseMoved(ev);
		}

		@Override
		public void mouseExited(MouseEvent ev)
		{
			Object object = ev.getSource();
			if (object instanceof Window && savedCursor != null)
			{
				Window w = (Window) object;
				w.setCursor(savedCursor);
				savedCursor = null;
			}
		}

		@Override
		public void mouseClicked(MouseEvent ev)
		{
			if (titlePane == null || (ev.getClickCount() % 2) != 0
					|| (ev.getModifiersEx() & InputEvent.BUTTON1_MASK) == 0)
				return;
			Window w = (Window) ev.getSource();
			Point convertedPoint = SwingUtilities.convertPoint(w, ev.getPoint(),
					titlePane);
			if (!titlePane.contains(convertedPoint))
				return;
			Frame f = null;
			Dialog d = null;
			if (w instanceof Frame)
				f = (Frame) w;
			else if (w instanceof Dialog)
				d = (Dialog) w;
			if (f != null && !f.isResizable())
				return;
			if (d != null && !d.isResizable())
				return;
			if (titlePane.wasMaximized())
			{
				if (w.getX() == 0 && w.getY() == minScreenY)
					titlePane.restore();
				else
					w.setLocation(0, minScreenY);
			} else
				titlePane.maximize();
		}

		private int calculateCorner(Component c, int x, int y)
		{
			int xPosition = calculatePosition(x, c.getWidth());
			int yPosition = calculatePosition(y, c.getHeight());
			if (xPosition == -1 || yPosition == -1)
				return -1;
			return yPosition * 5 + xPosition;
		}

		private int getCursor(int corner)
		{
			if (corner == -1)
				return 0;
			return cursorMapping[corner];
		}

		private int calculatePosition(int spot, int width)
		{
			if (spot < BORDER_DRAG_THICKNESS)
				return 0;
			if (spot < CORNER_DRAG_WIDTH)
				return 1;
			if (spot >= (width - BORDER_DRAG_THICKNESS))
				return 4;
			if (spot >= (width - CORNER_DRAG_WIDTH))
				return 3;
			return 2;
		}
	}
	protected class WindowHandler extends WindowAdapter
	{
		@Override
		public void windowActivated(WindowEvent ev)
		{
			isActive = true;
			SwingUtilities.invokeLater(() -> paintBorder((Graphics2D) root.getGraphics()));

			// root.repaint();
		}

		@Override
		public void windowDeactivated(WindowEvent ev)
		{
			isActive = false;
			SwingUtilities.invokeLater(() -> paintBorder((Graphics2D) root.getGraphics()));
		}

		//		@Override
		//		public void windowLostFocus(WindowEvent e)
		//		{
		//			if (isFullScreenMode)
		//				getWindow().requestFocus();
		//		}
	}

	private final RenderingConfig renderingConfig = new RenderingConfig();
	private void paintBorder(Graphics2D g2d)
	{
		if (isFullScreenMode || g2d == null || window == null)
			return;
		renderingConfig.setAAPainting(g2d);
		Color saveColor = g2d.getColor();
		int w = root.getWidth(), h = root.getHeight();
		g2d.setColor(Color.gray);
		g2d.drawRoundRect(0, 0, w - 1, h - 1, arc, arc);
		g2d.setColor(isActive ? RThemeColor.transThemeColor : Color.gray);
		g2d.drawRoundRect(0, 0, w - 1, h - 1, arc + 3, arc + 3);
		g2d.setColor(saveColor);
		renderingConfig.resetAAPainting(g2d);
	}
}
